package wechat

import (
	"context"
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"errors"

	"github.com/gogf/gf/v2/crypto/gaes"
	"github.com/gogf/gf/v2/encoding/gjson"
	"github.com/gogf/gf/v2/frame/g"
)

var (
	ErrAppIDNotMatch       = errors.New("app id not match")
	ErrInvalidBlockSize    = errors.New("invalid block size")
	ErrInvalidPKCS7Data    = errors.New("invalid PKCS7 data")
	ErrInvalidPKCS7Padding = errors.New("invalid padding on input")
)

// WxUserInfo 微信用户返回值信息
type WxUserInfo struct {
	OpenID          string `json:"openId"`
	UnionID         string `json:"unionId"`
	NickName        string `json:"nickName"`
	Gender          int    `json:"gender"`
	City            string `json:"city"`
	Province        string `json:"province"`
	Country         string `json:"country"`
	AvatarURL       string `json:"avatarUrl"`
	Language        string `json:"language"`
	ParentNumber    string `json:"parentNumber"`
	PurePhoneNumber string `json:"purePhoneNumber"`
}

type WxSessionInfo struct {
	OpenID     string `json:"openId"`
	SessionKey string `json:"session_key"`
	UnionID    string `json:"unionId"`
	Errcode    int    `json:"errcode"`
	Errmsg     string `json:"errmsg"`
}

// Decrypt @Title 解析用户信息数据
func DecryptEncryptedData(sessionKey, encryptedData, iv string) (*WxUserInfo, error) {
	aesKey, err := base64.StdEncoding.DecodeString(sessionKey)
	if err != nil {
		return nil, err
	}

	cipherText, err := base64.StdEncoding.DecodeString(encryptedData)
	if err != nil {
		return nil, err
	}

	ivBytes, err := base64.StdEncoding.DecodeString(iv)
	if err != nil {
		return nil, err
	}

	block, err := aes.NewCipher(aesKey)
	if err != nil {
		return nil, err
	}

	mode := cipher.NewCBCDecrypter(block, ivBytes)
	mode.CryptBlocks(cipherText, cipherText)

	cipherText, err = gaes.PKCS5UnPadding(cipherText, block.BlockSize())
	if err != nil {
		return nil, err
	}

	wxUserInfo := WxUserInfo{}
	err = gjson.DecodeTo(cipherText, &wxUserInfo)
	if err != nil {
		return nil, err
	}

	return &wxUserInfo, nil
}

// 管理端解密
func AdminCode2Session(ctx context.Context, code string) (info *WxSessionInfo, err error) {
	appid, err := g.Cfg().Get(ctx, "wechatAdmin.appid")
	if err != nil {
		panic("获取微信配置失败-appid")
	}

	secret, err := g.Cfg().Get(ctx, "wechatAdmin.secret")
	if err != nil {
		panic("获取微信配置失败-secret")
	}

	url := "https://api.weixin.qq.com/sns/jscode2session"

	// 返回content为string类型
	content := g.Client().GetContent(
		ctx,
		url,
		g.Map{
			"appid":      appid,
			"secret":     secret,
			"js_code":    code,
			"grant_type": "authorization_code",
		})

	wxSessionInfo := WxSessionInfo{}
	err = gjson.DecodeTo(content, &wxSessionInfo)
	if err != nil {
		return nil, err
	}

	return &wxSessionInfo, nil
}

// 家长端解密
func ParentCode2Session(ctx context.Context, code string) (info *WxSessionInfo, err error) {
	appid, err := g.Cfg().Get(ctx, "wechatParent.appid")
	if err != nil {
		panic("获取微信配置失败-appid")
	}

	secret, err := g.Cfg().Get(ctx, "wechatParent.secret")
	if err != nil {
		panic("获取微信配置失败-secret")
	}

	url := "https://api.weixin.qq.com/sns/jscode2session"

	// 返回content为string类型
	content := g.Client().GetContent(
		ctx,
		url,
		g.Map{
			"appid":      appid,
			"secret":     secret,
			"js_code":    code,
			"grant_type": "authorization_code",
		})

	wxSessionInfo := WxSessionInfo{}
	err = gjson.DecodeTo(content, &wxSessionInfo)
	if err != nil {
		return nil, err
	}

	return &wxSessionInfo, nil
}
